﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.IO.Ports;
using System.Xml;
using Insteon;
using System.Collections.Concurrent;
using System.Configuration;
using Scheduling;
using Interfaces;

namespace Mesne
{
    public class Server
    {
        private Uri uri = new Uri(AppSettings.Communication.Uri);
        private ServiceHost svcHost;
        private IScheduler scheduler;
        private IExecutor executor;
        private IResponder responder;
        private ISystemConfiguration systemConfig;
        private string receiveString;


        private static object syncLock = new object();
        private object recieveLock = new object();
        private static Server instance;

        public static Server Instance
        {
            get
            {
                lock (syncLock)
                {
                    if (instance == null)
                    {
                        instance = new Server();
                    }
                    return instance;
                }
            }
        }

        private Server()
        {
            InitializeServer();
        }

        private void InitializeServer()
        {
            try
            {
                CreateComponents();
                LoadInformation();
                SubscribeToEvents();
            }
            catch(Exception ex)
            {
                System.IO.StreamWriter writer = new System.IO.StreamWriter(@"C:\mesne\mesne.log");
                writer.Write(ex.ToString());
                writer.Close();
            }
        }

        private void CreateComponents()
        {
            systemConfig = new SystemConfiguration();
            scheduler = new Scheduler(10000, systemConfig);
            executor = new Executor();
            responder = new Responder();
        }

        private void LoadInformation()
        {
            scheduler.LoadOperations(systemConfig.GetOperations());
        }

        private void SubscribeToEvents()
        {
            scheduler.Tripped += scheduler_Tripped;
            systemConfig.ErrorOccured += systemConfig_ErrorOccured;
            executor.ResultReturned += executor_ResultReturned;
        }

        void executor_ResultReturned(string arg1, string arg2)
        {
            responder.Add(new RoutedMessage() { Message = string.Format("Device:{0} Level: {1}", arg1, arg2) });
        }

        void systemConfig_ErrorOccured(string sessionId, string message)
        {
            if (responder != null)
            {
                responder.Add(new RoutedMessage() { Message = message, SessionId = sessionId });
            }
        }        

        void scheduler_Tripped(ICommand obj)
        {
            responder.Add(new RoutedMessage() { Message = "Scheduled: " + obj.HexString });
            executor.Add(obj);
        }

        public void Start()
        {
            if (svcHost == null)
            {                
                svcHost = new ServiceHost(typeof(ServiceProxy), new Uri[] { uri });
                svcHost.AddServiceEndpoint(typeof(IServiceContract), new MesneBinding(), "tcp");
                //Serial.Port.DataReceived += Port_DataReceived;
                ServiceDebugBehavior debug = svcHost.Description.Behaviors.Find<ServiceDebugBehavior>();

                // if not found - add behavior with setting turned on 
                if (debug == null)
                {
                    svcHost.Description.Behaviors.Add(
                         new ServiceDebugBehavior() { IncludeExceptionDetailInFaults = true });
                }
                else
                {
                    // make sure setting is turned ON
                    if (!debug.IncludeExceptionDetailInFaults)
                    {
                        debug.IncludeExceptionDetailInFaults = true;
                    }
                }

                svcHost.Open();
            }
        }

        void Port_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            lock (recieveLock)
            {
                int byteNum = Serial.Port.BytesToRead;
                try
                {
                    if (byteNum > 0)
                    {
                        
                        byte[] bytes = new byte[byteNum];
                        Serial.Port.Read(bytes, 0, bytes.Length);
                        receiveString += BitConverter.ToString(bytes);
                        ProcessResponse(receiveString);
                        //string receipt = 
                        //responder.Add(new RoutedMessage() { Message = receipt });
                    }
                }
                catch (Exception ex)
                {
                    System.IO.StreamWriter writer = new System.IO.StreamWriter(@"C:\mesne\mesne.log");
                    writer.WriteLine(ex.ToString());
                    writer.Close();
                }
            }
        }

        private void ProcessResponse(string response)
        {
            string[] parts = response.Split('-');

        }

        public void Shutdown()
        {
            svcHost.Close();
            Serial.Port.Close();
        }

        public void SendMessage(ICommand command)
        {
            executor.Add(command);
        }        

        public List<IOperation> GetOperations()
        {
            return scheduler.GetOperations();
        }

        public string GetConfig()
        {
            return systemConfig.GetConfig();
        }

        public List<IDevice> GetDevices(string sessionId)
        {
            return systemConfig.GetDevices(sessionId);
        }

        public bool ScheduleOperation(IOperation operation)
        {
            return scheduler.Add(operation);
        }

        public bool ScheduleOperations(List<IOperation> operations)
        {
            return scheduler.Add(operations);
        }

        public bool EstablishCallback(string sessionId, IClientCallback callback)
        {
            bool result = false;
            try
            {
                responder.RegisterCallback(sessionId, callback);
                result = true;
            }
            catch
            {
                result = false;
            }
            return result;
        }

        public void RemoveCallback(string sessionId)
        {
            responder.DeregisterCallback(sessionId);
        }

        public bool DeleteOperation(string name)
        {
            return scheduler.DeleteOperation(name);
        }

        public bool DeleteOperations(List<string> names)
        {
            return scheduler.DeleteOperations(names);
        }

        public bool DeleteAllOperations()
        {
            return scheduler.DeleteOperations();
        }
    }
}
